package com.ruben.simplescaffold.plugin;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.update.Update;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.mapper.Mapper;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruben.simplescaffold.entity.UserDetail;
import com.ruben.simplescaffold.pojo.common.BaseEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.Consts;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Mybatis拦截器
 *
 * @author <achao1441470436@gmail.com>
 * @since 2022/3/31 22:22
 */
@Component
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class MybatisExecutorUpdatePlugin implements Interceptor {
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		Object[] args = invocation.getArgs();
		MappedStatement ms = (MappedStatement) args[0];
		if (SqlCommandType.INSERT.equals(ms.getSqlCommandType())) {
			// 新增
			fillTime(args[1], UserDetail::getGmtCreate);
			fillTime(args[1], UserDetail::getGmtModified);
		}
		if (SqlCommandType.UPDATE.equals(ms.getSqlCommandType()) && args[1] instanceof MapperMethod.ParamMap) {
			// 更新
			MapperMethod.ParamMap<?> paramMap = (MapperMethod.ParamMap<?>) args[1];
			Object bean = paramMap.get(Constants.ENTITY);
			if (paramMap.containsKey(Constants.WRAPPER)) {
				// 通过Wrapper更新
				Object wrapper = paramMap.get(Constants.WRAPPER);
				if (wrapper instanceof AbstractWrapper && wrapper instanceof Update) {
					String fieldName = LambdaUtil.getFieldName(UserDetail::getGmtModified);
					String mapperClassName = ms.getId().substring(0, ms.getId().lastIndexOf('.'));
					Class<?> entityClass = ReflectionKit.getSuperClassGenericType(Class.forName(mapperClassName), Mapper.class, 0);
					List<TableFieldInfo> fieldList = TableInfoHelper.getTableInfo(entityClass).getFieldList();
					String columnName = fieldList.stream().filter(fieldInfo -> fieldInfo.getProperty().equals(fieldName)).findAny().map(TableFieldInfo::getColumn).orElseThrow(() -> new MybatisPlusException("未找到字段" + fieldName));
					final Map<String, Object> paramNameValuePairs = ((AbstractWrapper<?, ?, ?>) wrapper).getParamNameValuePairs();
					String placeholder = "#" + fieldName + "#";
					paramNameValuePairs.put(placeholder, LocalDateTime.now());
					((Update<?, ?>) wrapper).setSql(String.format("%s = #{%s.%s}", columnName, "ew.paramNameValuePairs", placeholder));
				}
			} else if (Objects.nonNull(bean)) {
				// 通过id更新
				fillTime(bean, UserDetail::getGmtModified);
			}
		}
		return invocation.proceed();
	}

	/**
	 * 给bean填充时间
	 *
	 * @param bean 对象
	 * @param func 填充方法，只用于获取属性名
	 * @param <T>  对象类型
	 */
	private <T> void fillTime(Object bean, Func1<T, ?> func) {
		String fieldName = LambdaUtil.getFieldName(func);
		if (ReflectUtil.hasField(bean.getClass(), fieldName)) {
			BeanUtil.setFieldValue(bean, fieldName, LocalDateTime.now());
		}
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}
}